/* //device/system/broncho-ril/atchannel.c
**
** Copyright 2006, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
**     http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/


#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <ctype.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>
#include <termios.h>
#include <signal.h>
#include "cutils/properties.h"

#ifndef BRONCHO_TEST
#include <cutils/sockets.h>
#endif

#include <utils/Log.h>
#include "at_tok.h"
#include "ril_util.h"
#include "atchannel.h"
#undef LOG_TAG
#define LOG_TAG "AT"

#ifdef HAVE_ANDROID_OS
/* for IOCTL's */
#include <linux/omap_csmi.h>
#endif /*HAVE_ANDROID_OS*/

#ifdef HAVE_ANDROID_OS
#define USE_NP 1
#endif /* HAVE_ANDROID_OS */


#define NUM_ELEMS(x) (sizeof(x)/sizeof(x[0]))

#define MAX_AT_RESPONSE (8 * 1024)
#define HANDSHAKE_RETRY_COUNT 8
#define HANDSHAKE_TIMEOUT_MSEC 1000
#define SMS_TIMEOUT_MSEC		100000


struct _ATChannel
{
	int					fd;
	int					cmd_fds[2];
	RilPortType			port_type;
	char*				port_name;
	char				at_buffer[MAX_AT_RESPONSE + 1];
	char*				buffer_cur;
	char*				sms_line1;

	pthread_t    		reader_tid;
	pthread_mutex_t		mutex;
	pthread_cond_t		cond;

	ATCommandType		type;
	const char*			resp_prefix;
	const char*			sms_pdu;
	long long			timeoutMsec;
	ATResponse*			p_resp;
	int					reader_closed;
	int					destroyed;

	ATUnsolHandler		on_unsol;
	void*				unsol_data;
	ATTimeoutHandler	on_timeout;
	void*				timeout_data;
	ATCloseHandler		on_closed;
	void*				closed_data;

	int                 first_log;
	int 				enable_at_log;
};

/**
 * returns 1 if line is a final response indicating error
 * See 27.007 annex B
 * WARNING: NO CARRIER and others are sometimes unsolicited
 */
static const char * s_finalResponsesError[] = 
{
    "ERROR",
	"COMMAND NOT SUPPORT",
    "+CMS ERROR:",
    "+CME ERROR:",
    "NO CARRIER", /* sometimes! */
    "NO ANSWER",
    "NO DIALTONE",
};

/**
 * returns 1 if line is a final response indicating success
 * See 27.007 annex B
 * WARNING: NO CARRIER and others are sometimes unsolicited
 */
static const char * s_finalResponsesSuccess[] = 
{
    "OK",
    "CONNECT"       /* some stacks start up data on another channel */
};

/**
 * returns 1 if line is the first line in (what will be) a two-line
 * SMS unsolicited response
 */
static const char * s_smsUnsoliciteds[] = {
    "+CMT:",
    "+CDS:",
    "+CBM:"
};

void  at_channel_dump_log(ATChannel* thiz, const char*  prefix, const char*  buff, int  len)
{
    if (len < 0)
        len = strlen(buff);

	if(thiz->enable_at_log)
	{
    	ALOGD("%s: %.*s", thiz->port_name, len, buff);
	}
}

static void at_channel_on_reader_closed(ATChannel* thiz);
static int at_channel_write_ctrlz (ATChannel* thiz, const char * sms_pdu);
static int at_channel_write_line (ATChannel* thiz, const char *s);
static void at_channel_clear_pending_command(ATChannel* thiz);

#ifndef USE_NP
static void set_timespec_relative(struct timespec *p_ts, long long msec)
{
    struct timeval tv;

    gettimeofday(&tv, (struct timezone *) NULL);

    /* what's really funny about this is that I know
       pthread_cond_timedwait just turns around and makes this
       a relative time again */
    p_ts->tv_sec = tv.tv_sec + (msec / 1000);
   	p_ts->tv_nsec = (tv.tv_usec + (msec % 1000) * 1000) * 1000;
	if (p_ts->tv_nsec >= 1000000000)
	{
		p_ts->tv_sec++;
		p_ts->tv_nsec -= 1000000000;
	}
}
#endif /*USE_NP*/


/** add an intermediate response to thiz->p_resp*/
static void at_response_add_intermediate(ATResponse* p_resp, const char *line)
{
    ATLine *p_new = NULL;

    p_new = (ATLine  *) malloc(sizeof(ATLine));
    p_new->line = strdup(line);

    /* note: this adds to the head of the list, so the list
       will be in reverse order of lines received. the order is flipped
       again before passing on to the command issuer */
	p_new->p_next = p_resp->p_intermediates;
	p_resp->p_intermediates = p_new;

	return;
}

static int is_final_response_error(const char *line)
{
    size_t i;

    for (i = 0 ; i < NUM_ELEMS(s_finalResponsesError) ; i++) 
	{
        if (strStartsWith(line, s_finalResponsesError[i])) 
		{
            return 1;
        }
    }

    return 0;
}

static int is_final_response_success(const char *line)
{
    size_t i;

    for (i = 0 ; i < NUM_ELEMS(s_finalResponsesSuccess) ; i++) 
	{
        if (strStartsWith(line, s_finalResponsesSuccess[i])) 
		{
            return 1;
        }
    }

    return 0;
}

/**
 * returns 1 if line is a final response, either  error or success
 * See 27.007 annex B
 * WARNING: NO CARRIER and others are sometimes unsolicited
 */
static int is_final_response(const char *line)
{
    return is_final_response_success(line) || is_final_response_error(line);
}


static int is_sms_unsolicited(const char *line)
{
    size_t i;

    for (i = 0 ; i < NUM_ELEMS(s_smsUnsoliciteds) ; i++) 
	{
        if (strStartsWith(line, s_smsUnsoliciteds[i])) 
		{
            return 1;
        }
    }

    return 0;
}

/** assumes thiz->mutex is held */
static void at_channel_write_log_data(ATChannel* thiz, const char* data, int data_len)
{
	char path[128];
	FILE* fp = NULL;

	if(thiz != NULL && data != NULL && data_len > 0)
	{
		mkdir("/data/ril/", 0777);
		snprintf(path, 128, "/data/ril/%d/", getpid());
		mkdir(path, 0777);
		snprintf(path, 128, "/data/ril/%d/dev/", getpid());
		mkdir(path, 0777);
		snprintf(path, 128, "/data/ril/%d/%s.txt", getpid(), thiz->port_name);
		if(thiz->first_log) 
		{
			fp = fopen(path, "wb+");
		} 
		else 
		{
			fp = fopen(path, "ab+");
		}
		if(fp != NULL) {
			fwrite(data, data_len, 1, fp);
			fclose(fp);
			thiz->first_log = 0;
		}
	}

	return;
}
/** assumes thiz->mutex is held */
static void at_channel_handle_final_response(ATChannel* thiz, const char *line)
{
	thiz->p_resp->finalResponse = strdup(line);
    pthread_cond_signal(&thiz->cond);

	return;
}

static void at_channel_handle_unsolicited(ATChannel* thiz, const char *line)
{
	if (thiz->on_unsol != NULL)
	{
        thiz->on_unsol(thiz, line, NULL, thiz->unsol_data);
    }

	return;
}

static void at_channel_process_line(ATChannel* thiz, const char *line)
{
	if(thiz->enable_at_log)
	{
		//ALOGD("at_channel_process_line: line=%s, thiz=%p, resp=%p\n", line, thiz, thiz->p_resp);
	}

    if (thiz->p_resp == NULL) 
	{
        /* no command pending */
        at_channel_handle_unsolicited(thiz, line);
    } 
	else if (is_final_response_success(line)) 
	{
        thiz->p_resp->success = 1;
        at_channel_handle_final_response(thiz, line);
    } 
	else if (is_final_response_error(line)) 
	{
        thiz->p_resp->success = 0;
        at_channel_handle_final_response(thiz, line);
    } 
	else if (thiz->sms_pdu != NULL && 0 == strcmp(line, "> ")) 
	{
        // See eg. TS 27.005 4.3
        // Commands like AT+CMGS have a "> " prompt
        at_channel_write_ctrlz(thiz, thiz->sms_pdu);
        thiz->sms_pdu = NULL;
    } 
	else 
	{
		switch (thiz->type) 
		{
        case NO_RESULT:
            at_channel_handle_unsolicited(thiz, line);
            break;

        case NUMERIC:
            if (thiz->p_resp->p_intermediates == NULL
                && isdigit(line[0])) 
			{
                at_response_add_intermediate(thiz->p_resp, line);
            } 
			else 
			{
                /* either we already have an intermediate response or
                   the line doesn't begin with a digit */
                at_channel_handle_unsolicited(thiz, line);
            }
            break;

        case SINGLELINE:
            if (thiz->p_resp->p_intermediates == NULL
                && strStartsWith (line, thiz->resp_prefix)) 
			{
                at_response_add_intermediate(thiz->p_resp, line);
            } 
			else 
			{
                /* we already have an intermediate response */
                at_channel_handle_unsolicited(thiz, line);
            }
            break;

        case MULTILINE:
            if (strStartsWith (line, thiz->resp_prefix)) 
			{
                at_response_add_intermediate(thiz->p_resp, line);
            } 
			else 
			{
                at_channel_handle_unsolicited(thiz, line);
            }
        	break;

        default: /* this should never be reached */
            ALOGE("Unsupported AT command type %d\n", thiz->type);
            at_channel_handle_unsolicited(thiz, line);
        	break;
		}
    }
}


/**
 * Returns a pointer to the end of the next line
 * special-cases the "> " SMS prompt
 *
 * returns NULL if there is no complete line
 */
static char * find_next_eol(char *cur)
{
    if (cur[0] == '>' && cur[1] == ' ' && cur[2] == '\0') 
	{
        /* SMS prompt character...not \r terminated */
        return cur+2;
    }

    // Find next newline
    while (*cur != '\0' && *cur != '\r') cur++;
    
	return *cur == '\0' ? NULL : cur;
}

/**
 * Reads a line from the AT channel, returns NULL on timeout.
 * Assumes it has exclusive read access to the FD
 *
 * This line is valid only until the next call to at_channel_readline
 *
 * This function exists because as of writing, android libc does not
 * have buffered stdio.
 */
static char *at_channel_readline(ATChannel* thiz)
{
    char *p_eol = NULL;
    char *ret = NULL;

	/*TODO: */
	static char addLine[3] = {0};
	//it will be cleared in at_channel_handle_data,search word memset.
	if(addLine[0] != '\0')
	{
		ret = addLine;
		return ret;
	}

    /* this is a little odd. I use *thiz->buffer_cur == 0 to
     * mean "buffer consumed completely". If it points to a character, than
     * the buffer continues until a \0
     */
    if (*thiz->buffer_cur == '\0') 
	{
        /* empty buffer */
        thiz->buffer_cur = thiz->at_buffer;
        *thiz->buffer_cur = '\0';
    } 
	else 
	{   /* *thiz->buffer_cur != '\0' */
        /* there's data in the buffer from the last read */

        // skip over leading newlines
        while (*thiz->buffer_cur == '\r' 
			|| *thiz->buffer_cur == '\n')
		{
            thiz->buffer_cur++;
		}
        p_eol = find_next_eol(thiz->buffer_cur);
        if (p_eol == NULL) 
		{
            /* a partial line. move it up and prepare to read more */
            size_t len = 0;

            len = strlen(thiz->buffer_cur);
            memmove(thiz->at_buffer, thiz->buffer_cur, len + 1);
            thiz->buffer_cur = thiz->at_buffer;
        }
		else
		{
			ret = thiz->buffer_cur;
			*p_eol = '\0';
			thiz->buffer_cur = p_eol + 1;
			if(thiz->enable_at_log)
			{
				ALOGD("AT< %s\n", ret);
			}
		}
    }

	if(thiz->reader_tid != pthread_self())
	{
	    ret = NULL;
	}
	
	/*TODO: think-will bug*/
	if(ret != NULL && strStartsWith(ret, "^CPBR:"))
	{
		strcpy(addLine, "OK");
	}

	/*TODO: spd bug*/
	if(ret != NULL && strStartsWith(ret, "+CSYS:"))
	{
		strcpy(addLine, "OK");
	}


	return ret;
}

static int at_channel_handle_data(ATChannel* thiz)
{
	ssize_t count = 0;
	int ret = 0;
    char *p_read = NULL;

	pthread_mutex_lock(&thiz->mutex);
	p_read = thiz->at_buffer + strlen(thiz->at_buffer);
	count = read(thiz->fd, p_read,
				MAX_AT_RESPONSE - (p_read - thiz->at_buffer));
#ifdef ENABLE_LOGFILE
	at_channel_write_log_data(thiz, p_read, count);
#endif

#if 0
	PrintBuffer("rild serial data: ", p_read, count);
#endif

	if(count > 0)
	{
		char* line = NULL;

		p_read[count] = '\0';
		while((line = at_channel_readline(thiz)) != NULL)
		{
			if(thiz->sms_line1 != NULL)
			{
				if (thiz->on_unsol != NULL) 
				{
					thiz->on_unsol(thiz, thiz->sms_line1, line, thiz->unsol_data);
	            }
				free(thiz->sms_line1);
				thiz->sms_line1 = NULL;
			}
			else if(is_sms_unsolicited(line))
			{
				thiz->sms_line1 = strdup(line);
			}
			else
			{
				at_channel_process_line(thiz, line);
			}
			memset(line, 0, strlen(line));
		}

		ret = 1;
	}
	else
	{
		/* read error encountered or EOF reached */
		if(count == 0) 
		{
			ALOGD("atchannel: EOF reached");
		} 
		else 
		{
			ALOGD("atchannel: read error %s", strerror(errno));
		}
	}
	pthread_mutex_unlock(&thiz->mutex);

	return ret;
}

static void at_channel_real_destroy(ATChannel* thiz)
{
	if(thiz->fd > 0)
	{
		ALOGD("%s close fd:%d!!", __func__, thiz->fd);
		close(thiz->fd);
		thiz->fd = -1;
	}
	
	if(thiz->port_name != NULL)
	{
		ALOGD("%s free %s!!", __func__, thiz->port_name);
		free(thiz->port_name);
		thiz->port_name = NULL;
	}

	free(thiz);

	return;
}

static void at_channel_on_reader_closed(ATChannel* thiz)
{
	pthread_mutex_lock(&thiz->mutex);
    if (thiz->reader_closed == 0) 
	{
        thiz->reader_closed = 1;
        pthread_cond_signal(&thiz->cond);
		if(thiz->closed_data != NULL)
		{
			thiz->on_closed(thiz, thiz->closed_data);
		}
    }
	pthread_mutex_unlock(&thiz->mutex);

	return;
}

static void *at_channel_reader_mainloop(void *arg)
{
	ATChannel* thiz = (ATChannel*)arg;

    for (;;) 
	{
		fd_set read_fd;
		int max_fd = 0;

		FD_ZERO(&read_fd);
		FD_SET (thiz->fd, &read_fd);
		FD_SET (thiz->cmd_fds[0], &read_fd);
		max_fd = thiz->fd > thiz->cmd_fds[0] ? thiz->fd : thiz->cmd_fds[0];

		select (max_fd + 1, &read_fd, NULL, NULL, NULL);
		if(FD_ISSET(thiz->fd, &read_fd))
		{
			if(at_channel_handle_data(thiz) == 0)
			{
				break;
			}
		}
		else if(FD_ISSET(thiz->cmd_fds[0], &read_fd))
		{
			int count = 0;
			char cmd[64] = {0};
			
			if((count = read(thiz->cmd_fds[0], cmd, 64)) > 0)
			{
				if(thiz->enable_at_log)
				{
					ALOGD("channle cmd: %s\n", cmd);
				}
				if(strcmp(cmd, "EXIT") == 0)
				{
					write(thiz->cmd_fds[1], "OK", strlen("OK"));
					break;
				}
			}
		}
    }
    at_channel_on_reader_closed(thiz);
	ALOGD("%s: exit\n", __func__);
	pthread_exit(NULL);

    return NULL;
}

/**
 * Sends string s to the radio with a \r appended.
 * Returns AT_ERROR_* on error, 0 on success
 *
 * This function exists because as of writing, android libc does not
 * have buffered stdio.
 */
static int at_channel_write_line (ATChannel* thiz, const char *s)
{
    size_t cur = 0;
    size_t len = strlen(s);
    ssize_t written = 0;
	char* cmd = s;

    if (thiz->fd < 0 || thiz->reader_closed > 0) 
	{
        return AT_ERROR_CHANNEL_CLOSED;
    }

    at_channel_dump_log(thiz, ">> ", s, strlen(s) );

    /* the main string */
    while (cur < len) 
	{
        do
		{
            written = write (thiz->fd, cmd + cur, len - cur);
        } while (written < 0 && errno == EINTR);

        if (written < 0) 
		{
			ALOGD("at_channel_write_line: written=%d\n", written);
            return AT_ERR_WRITE_DATA_ERROR;
        }

        cur += written;
    }

#ifdef ENABLE_LOGFILE
	at_channel_write_log_data(thiz, cmd, len);
#endif

    /* the \r  */
	do 
	{
        written = write (thiz->fd, "\r" , 1);
    } while ((written < 0 && errno == EINTR) || (written == 0));

    if (written < 0) 
	{
        return AT_ERROR_GENERIC;
    }

    return 0;
}

static int at_channel_write_ctrlz (ATChannel* thiz, const char* sms_pdu)
{
    size_t cur = 0;
    size_t len = strlen(sms_pdu);
    ssize_t written;
    char data[512];

    if (thiz->fd < 0 || thiz->reader_closed > 0) 
	{
        return AT_ERROR_CHANNEL_CLOSED;
    }

//    ALOGD("AT> %s^Z\n", sms_pdu);
    at_channel_dump_log( thiz, ">* ", sms_pdu, strlen(sms_pdu) );
    snprintf(data, 512, "%s%c%c", sms_pdu, 0x1A, '\r');
    len++;
 //   ALOGD("SMS PDU: len=%d, data_len=%d\n", strlen(sms_pdu), len);

    /* the main string */
    while (cur < len) 
	{
        do 
		{
            written = write (thiz->fd, data + cur, len - cur);
        } while (written < 0 && errno == EINTR);

        if (written < 0) 
		{
            return AT_ERROR_GENERIC;
        }

        cur += written;
    }

    /* the ^Z  */
    /*
    do 
	{
		char strCtrlZ[2] = {0x1A, 0};
        written = write (thiz->fd, strCtrlZ, 1);
    } while ((written < 0 && errno == EINTR) || (written == 0));

    if (written < 0) 
	{
        return AT_ERROR_GENERIC;
    }
    */

    return 0;
}

static void at_channel_clear_pending_command(ATChannel* thiz)
{
    if (thiz->p_resp != NULL) 
	{
        at_response_free(thiz->p_resp);
    }

    thiz->p_resp = NULL;
    thiz->resp_prefix = NULL;
    thiz->sms_pdu = NULL;
}

/**
 * Starts AT handler on stream "fd'
 * returns 0 on success, -1 on error
 */
int at_channel_open(ATChannel* thiz)
{
	pthread_attr_t attr;
	int ret = 0;

	ALOGD("at_channel_open: thiz=%p", thiz);
	pthread_mutex_lock(&thiz->mutex);
	if(thiz->reader_closed)
	{
		if(thiz->fd > 0)
		{
			close(thiz->fd);
			thiz->fd = -1;
		}

		if(thiz->cmd_fds[0] > 0)
		{
			close(thiz->cmd_fds[0]);
			close(thiz->cmd_fds[1]);
			thiz->cmd_fds[0] = 0;
			thiz->cmd_fds[1] = 0;
		}

		if((thiz->fd = ril_open_port(thiz->port_type, 
							thiz->port_name)) > 0)
		{
			pipe(thiz->cmd_fds);
			pthread_attr_init (&attr);
			pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
			if((ret = pthread_create(&thiz->reader_tid, &attr, at_channel_reader_mainloop, thiz)) == 0)
			{
				thiz->reader_closed = 0;
			}
			ALOGD("at_channel_open: tid=%ld\n", thiz->reader_tid);
		}
	}
	pthread_mutex_unlock(&thiz->mutex);

	return (thiz->reader_closed == 0);
}

ATChannel* 	at_channel_create(RilPortType type, const char* port_name, 
				ATUnsolHandler on_unsol, void* unsol_data)
{
    ATChannel* thiz = NULL;
	char at_logable[PROPERTY_VALUE_MAX] = {0};

    ALOGD("at_channel_create: device=%s\n", port_name);
	thiz = calloc(1, sizeof (ATChannel));
	thiz->port_type = type;
	thiz->port_name = strdup(port_name);
	thiz->on_unsol = on_unsol;
	thiz->unsol_data = unsol_data;
	thiz->reader_closed = 1;
	thiz->destroyed = 0;
	thiz->resp_prefix = NULL;
	thiz->sms_pdu = NULL;
	thiz->p_resp = NULL;
	thiz->buffer_cur = thiz->at_buffer;
	pthread_mutex_init(&thiz->mutex, NULL);
	pthread_cond_init(&thiz->cond, NULL);
	at_channel_open(thiz);

	thiz->first_log = 1;

	property_get("sys.at_log", at_logable, "");
	if(strcmp(at_logable, "1") == 0)
	{
		thiz->enable_at_log = 1;
	}
#ifdef ENABLE_LOG
	thiz->enable_at_log = 1;
#endif

	return thiz;
}

void at_channel_close (ATChannel* thiz)
{
	pthread_mutex_lock(&thiz->mutex);
	at_channel_clear_pending_command(thiz);
	
	ALOGD("%s: pipe fd fd[0]=%d, fd[1]=%d", __func__, thiz->cmd_fds[0], thiz->cmd_fds[1]);
	if(thiz->cmd_fds[0] > 0)
	{
		fd_set fds;
		struct timeval tv ;
		int retry_times = 0;
	
		while(1)
		{
			write(thiz->cmd_fds[1], "EXIT", strlen("EXIT"));
			sleep(1);

			FD_ZERO(&fds);
			FD_SET(thiz->cmd_fds[0], &fds);
			tv.tv_sec = 10;
			tv.tv_usec = 0;
		
			select(thiz->cmd_fds[0] + 1, &fds, NULL, NULL, &tv);
			if(FD_ISSET(thiz->cmd_fds[0], &fds))
			{
				char    resp[64] ={0};
				int     resp_len = 0;

				if ((resp_len = read(thiz->cmd_fds[0], resp, 64)) > 0 
						&& strncmp(resp, "OK", strlen("OK")) == 0)
				{
					ALOGD("%s: exit safe!!", __func__);
					break;
				}
				ALOGD("%s: resp=%s", __func__, resp);
			}

			retry_times++;
			if(retry_times > 2)
			{
				break;
			}
		}
		close(thiz->cmd_fds[0]);
		close(thiz->cmd_fds[1]);
		thiz->cmd_fds[0] = 0;
		thiz->cmd_fds[1] = 0;
	}

	if (thiz->fd > 0) 
	{
        close(thiz->fd);
		thiz->fd = 0;
		ALOGE("%s: close fd\n", __func__);
    }

	if(thiz->sms_line1 != NULL)
	{
		free(thiz->sms_line1);
		thiz->sms_line1 = NULL;
	}

    thiz->fd = -1;
    thiz->reader_closed = 1;
	thiz->reader_tid = 0;
	thiz->buffer_cur = thiz->at_buffer;
	memset(thiz->at_buffer, 0, MAX_AT_RESPONSE + 1);
    pthread_cond_signal(&thiz->cond);
    pthread_mutex_unlock(&thiz->mutex);
	ALOGD("%s: end\n", __func__);

	return;
}

void at_channel_destroy(ATChannel* thiz)
{
	if(thiz != NULL)
	{
		at_channel_close(thiz);
		thiz->destroyed = 1;
		at_channel_real_destroy(thiz);
	}
	sleep(1);

	return;
}

static ATResponse * at_response_new()
{
    return (ATResponse *) calloc(1, sizeof(ATResponse));
}

void at_response_free(ATResponse *p_response)
{
    ATLine *p_line = NULL;

    if (p_response == NULL) return;

    p_line = p_response->p_intermediates;
    while (p_line != NULL) 
	{
        ATLine *p_toFree;

        p_toFree = p_line;
        p_line = p_line->p_next;

        free(p_toFree->line);
        free(p_toFree);
    }

    free (p_response->finalResponse);
    free (p_response);
	
	return;
}

/**
 * The line reader places the intermediate responses in reverse order
 * here we flip them back
 */
static void reverse_intermediates(ATResponse *p_response)
{
    ATLine *pcur,*pnext;

    pcur = p_response->p_intermediates;
    p_response->p_intermediates = NULL;

    while (pcur != NULL) 
	{
        pnext = pcur->p_next;
        pcur->p_next = p_response->p_intermediates;
        p_response->p_intermediates = pcur;
        pcur = pnext;
    }
}

/**
 * Internal send_command implementation
 * Doesn't lock or call the timeout callback
 *
 * timeoutMsec == 0 means infinite timeout
 */

static int at_send_command_full_nolock (ATChannel* thiz,
					const char *command, ATCommandType type,
                    const char *responsePrefix, const char *smspdu,
                    long long timeoutMsec, ATResponse **pp_outResponse)
{
    int err = 0;
#ifndef USE_NP
    struct timespec ts;
#endif /*USE_NP*/

    if(thiz->p_resp != NULL) 
	{
        err = AT_ERROR_COMMAND_PENDING;
        goto error;
    }

    err = at_channel_write_line (thiz, command);
    if (err < 0) 
	{
        goto error;
    }

    thiz->type = type;
    thiz->resp_prefix = responsePrefix;
    thiz->sms_pdu = smspdu;
    thiz->p_resp = at_response_new();
	
	timeoutMsec = (timeoutMsec == 0) ? DEFAULT_AT_TIMEOUT_MSEC : timeoutMsec;
#ifndef USE_NP
    if (timeoutMsec != 0) 
	{
        set_timespec_relative(&ts, timeoutMsec);
    }
#endif /*USE_NP*/

    while (thiz->p_resp != NULL && thiz->p_resp->finalResponse == NULL && thiz->reader_closed == 0) 
	{
        if (timeoutMsec != 0) 
		{
#ifdef USE_NP
            err = pthread_cond_timeout_np(&thiz->cond, &thiz->mutex, timeoutMsec);
#else
            err = pthread_cond_timedwait(&thiz->cond, &thiz->mutex, &ts);
#endif /*USE_NP*/
        } 
		else 
		{
            err = pthread_cond_wait(&thiz->cond, &thiz->mutex);
        }

        if (err == ETIMEDOUT) {
            err = AT_ERROR_TIMEOUT;
            goto error;
        }
    }
	
    if (pp_outResponse == NULL) 
	{
        at_response_free(thiz->p_resp);
    } 
	else 
	{
        /* line reader stores intermediate responses in reverse order */
        reverse_intermediates(thiz->p_resp);
        *pp_outResponse = thiz->p_resp;
    }
    thiz->p_resp = NULL;

    if(thiz->reader_closed > 0) 
	{
        err = AT_ERROR_CHANNEL_CLOSED;
        goto error;
    }

    err = 0;
error:
    at_channel_clear_pending_command(thiz);

    return err;
}

/**
 * Internal send_command implementation
 *
 * timeoutMsec == 0 means infinite timeout
 */
static int at_send_command_full (ATChannel* thiz,
					const char *command, ATCommandType type,
                    const char *responsePrefix, const char *smspdu,
                    long long timeoutMsec, ATResponse **pp_outResponse)
{
    int err;
	int retryTimes = 1;

	if(thiz == NULL)
	{
		return AT_ERROR_CHANNEL_CLOSED;
	}

	if(thiz->reader_closed)
	{
		return AT_ERROR_CHANNEL_CLOSED;
	}

    if (0 != pthread_equal(thiz->reader_tid, pthread_self())) 
	{
        /* cannot be called from reader thread */
        return AT_ERROR_INVALID_THREAD;
    }
	
	do {
    	pthread_mutex_lock(&thiz->mutex);
	    err = at_send_command_full_nolock(thiz, command, type,
                    responsePrefix, smspdu,
                    timeoutMsec, pp_outResponse);
    	pthread_mutex_unlock(&thiz->mutex);

		if(err == AT_ERROR_TIMEOUT && retryTimes > 0)
		{
			retryTimes--;
		}
		else
		{
			break;
		}
	} while(1);

    if (err == AT_ERROR_TIMEOUT && thiz->on_timeout != NULL) 
	{
        thiz->on_timeout(thiz, thiz->timeout_data);
    }
	
    return err;
}


/**
 * Issue a single normal AT command with no intermediate response expected
 *
 * "command" should not include \r
 * pp_outResponse can be NULL
 *
 * if non-NULL, the resulting ATResponse * must be eventually freed with
 * at_response_free
 */
int at_send_command (ATChannel* thiz, const char *command, 
			long long timeoutMsec, ATResponse **pp_outResponse)
{
    int err;

    err = at_send_command_full (thiz, command, NO_RESULT, NULL,
                                    NULL, timeoutMsec, pp_outResponse);

    return err;
}


int at_send_command_singleline (ATChannel* thiz,
								const char *command,
								long long timeoutMsec,
                                const char *responsePrefix,
                                 ATResponse **pp_outResponse)
{
    int err;

    err = at_send_command_full (thiz, command, SINGLELINE, responsePrefix,
                                    NULL, timeoutMsec, pp_outResponse);

    if (err == 0 && pp_outResponse != NULL
        && (*pp_outResponse)->success > 0
        && (*pp_outResponse)->p_intermediates == NULL) 
	{
        /* successful command must have an intermediate response */
        at_response_free(*pp_outResponse);
        *pp_outResponse = NULL;
        return AT_ERROR_INVALID_RESPONSE;
    }

    return err;
}


int at_send_command_numeric (ATChannel* thiz,
							const char *command,
							long long timeoutMsec,
                             ATResponse **pp_outResponse)
{
    int err;

    err = at_send_command_full (thiz, command, NUMERIC, NULL,
                                    NULL, timeoutMsec, pp_outResponse);

    if (err == 0 && pp_outResponse != NULL
        && (*pp_outResponse)->success > 0
        && (*pp_outResponse)->p_intermediates == NULL) 
	{
        /* successful command must have an intermediate response */
        at_response_free(*pp_outResponse);
        *pp_outResponse = NULL;
        return AT_ERROR_INVALID_RESPONSE;
    }

    return err;
}


int at_send_command_sms (ATChannel* thiz,
						const char *command,
						const char *pdu,
						const char *responsePrefix,
						ATResponse **pp_outResponse)
{
    int err;

    err = at_send_command_full (thiz, command, SINGLELINE, responsePrefix,
                                    pdu, SMS_TIMEOUT_MSEC, pp_outResponse);

    if (err == 0 && pp_outResponse != NULL
        && (*pp_outResponse)->success > 0
        && (*pp_outResponse)->p_intermediates == NULL) 
	{
        /* successful command must have an intermediate response */
        at_response_free(*pp_outResponse);
        *pp_outResponse = NULL;
        return AT_ERROR_INVALID_RESPONSE;
    }

    return err;
}


int at_send_command_multiline (ATChannel* thiz,
								const char *command,
								long long timeoutMsec,
                                const char *responsePrefix,
                                ATResponse **pp_outResponse)
{
    int err;

    err = at_send_command_full (thiz, command, MULTILINE, responsePrefix,
                                    NULL, timeoutMsec, pp_outResponse);

    return err;
}

/**
 * Internal send_command implementation
 *
 * timeoutMsec == 0 means infinite timeout
 */
int at_send_command_no_resp (ATChannel* thiz, const char *command)
{
    int err;

	if(thiz == NULL)
	{
		return AT_ERROR_CHANNEL_CLOSED;
	}

	if(thiz->reader_closed)
	{
		return AT_ERROR_CHANNEL_CLOSED;
	}

    if (0 != pthread_equal(thiz->reader_tid, pthread_self())) 
	{
        /* cannot be called from reader thread */
        return AT_ERROR_INVALID_THREAD;
    }
	
   	pthread_mutex_lock(&thiz->mutex);
	err = at_channel_write_line (thiz, command);
	sleep(2);
	pthread_mutex_unlock(&thiz->mutex);

    return err;
}

/** This callback is invoked on the command thread */
void at_channel_set_on_timeout(ATChannel* thiz, ATTimeoutHandler on_timeout, void* timeout_data)
{
	if (thiz != NULL)
	{
		thiz->on_timeout = on_timeout;
		thiz->timeout_data = timeout_data;
	}

	return;
}

/**
 *  This callback is invoked on the reader thread (like ATUnsolHandler)
 *  when the input stream closes before you call at_close
 *  (not when you call at_close())
 *  You should still call at_close()
 */

void at_channel_set_on_closed(ATChannel* thiz, ATCloseHandler on_closed, void* closed_data)
{
    if (thiz != NULL)
	{
		thiz->on_closed = on_closed;
		thiz->closed_data = closed_data;
	}

	return;
}


/**
 * Periodically issue an AT command and wait for a response.
 * Used to ensure channel has start up and is active
 */
int at_handshake(ATChannel* thiz)
{
    int i;
    int err = 0;

    if (0 != pthread_equal(thiz->reader_tid, pthread_self())) 
	{
        /* cannot be called from reader thread */
        return AT_ERROR_INVALID_THREAD;
    }

    pthread_mutex_lock(&thiz->mutex);

    for (i = 0 ; i < HANDSHAKE_RETRY_COUNT ; i++) 
	{
        /* some stacks start with verbose off */
        err = at_send_command_full_nolock (thiz, "ATE0Q0V1", NO_RESULT,
                    NULL, NULL, HANDSHAKE_TIMEOUT_MSEC, NULL);

        if (err == 0) 
		{
            break;
        }
    }

    if (err == 0) 
	{
        /* pause for a bit to let the input buffer drain any unmatched OK's
           (they will appear as extraneous unsolicited responses) */
        sleep_msec(HANDSHAKE_TIMEOUT_MSEC);
    }

    pthread_mutex_unlock(&thiz->mutex);

    return err;
}

/**
 * Returns error code from response
 * Assumes AT+CMEE=1 (numeric) mode
 */
AT_CME_Error at_get_cme_error(const ATResponse *p_response)
{
    int ret;
    int err;
    char *p_cur;

    if (p_response->success > 0) 
	{
        return CME_SUCCESS;
    }

    if (p_response->finalResponse == NULL
        || !strStartsWith(p_response->finalResponse, "+CME ERROR:")) 
	{
        return CME_ERROR_NON_CME;
    }

    p_cur = p_response->finalResponse;
    err = at_tok_start(&p_cur);
    if (err < 0) 
	{
        return CME_ERROR_NON_CME;
    }

    err = at_tok_nextint(&p_cur, &ret);
    if (err < 0) 
	{
        return CME_ERROR_NON_CME;
    }

    return (AT_CME_Error) ret;
}

int at_command_exec_for_result(ATChannel* c, const char* cmd, long long timeoutMsec) 
{
	int ret = 1;
	ATResponse* p_outResponse = NULL;

	if (at_send_command(c, cmd, timeoutMsec, &p_outResponse) < 0 || p_outResponse->success == 0)
	{
		ret = 0;
	}
	else
	{
		ret = 1;
	}
	if(p_outResponse != NULL)
	{
	}
	else
	{
	}
	at_response_free (p_outResponse);

	return ret;
}

int at_command_exec_singleline_for_result(ATChannel* c, const char* cmd, 
					long long timeoutMsec, const char* prefix, ATResponse **pp_outResponse)
{
	int ret = 1;
	ATResponse* p_outResponse = NULL;

	if (at_send_command_singleline(c, cmd, timeoutMsec, prefix, &p_outResponse) < 0 
		|| p_outResponse->success == 0)
	{
		ret = 0;
	}
	else
	{
		ret = 1;
	}

	if (pp_outResponse != NULL)
	{
		*pp_outResponse = p_outResponse;
	}
	else
	{
		at_response_free (p_outResponse);
	}	

	return ret;
}
//wupeng add begin.
RIL_Errno       ril_get_error_from_at_error(AT_CME_Error error)
{
	RIL_Errno ret = RIL_E_GENERIC_FAILURE;

	switch(error)
	{
	case CME_ERROR_NON_CME:
		ret = RIL_E_GENERIC_FAILURE;
		break;

    case CME_SUCCESS:
		ret = RIL_E_SUCCESS;
		break;

	case CME_SIM_PIN2_REQUIRED:
		ret = RIL_E_SIM_PIN2;
		break;

	case CME_SIM_PUK2_REQUIRED:
	case CME_SIM_PINPIN2_BLOCK_ENABLE:
	case CME_SIM_PINPIN2_BLOCK_DISABLE:
		ret = RIL_E_SIM_PUK2;
		break;
//wupeng add begin.
	default:
		break;
//wupeng add end.
	}
	
	return ret;
}
//wupeng add end.
